Passed
Push — development ( 62539b...739f83 )
by Vad
10:43 queued 14s
created

BicyclesController.updateBatchPositions   A

Complexity

Conditions 1

Size

Total Lines 21
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 19
dl 0
loc 21
ccs 5
cts 5
cp 1
crap 1
rs 9.45
c 0
b 0
f 0
1 8
import {
2
  Controller,
3
  Get,
4
  Post,
5
  Param,
6
  Patch,
7
  Body,
8
  Query,
9
  BadRequestException,
10
} from '@nestjs/common';
11 8
import {
12
  ApiBearerAuth,
13
  ApiOperation,
14
  ApiResponse,
15
  ApiParam,
16
  ApiBody,
17
  ApiTags,
18
  ApiQuery,
19
} from '@nestjs/swagger';
20
// we have removed all JwtAuthGuards from this route.
21
//  import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
22 8
import { BicyclesService } from './bicycles.service';
23 8
import { UpdateBicycleDto } from './dto/update-bicycle.dto';
24 8
import { Bicycle } from './entities/bicycle.entity';
25
import { BicycleResponse } from './types/bicycle-response.interface';
26 8
import { CreateBicycleDto } from './dto/create-bicycle.dto';
27 8
import { CityName } from 'src/cities/types/city.enum';
28 8
import {
29
  BatchUpdateBicyclePositionsDto,
30
  BicycleBatchResponseDto,
31
  BicyclePositionDto,
0 ignored issues
show
introduced by
'BicyclePositionDto' is defined but never used.
Loading history...
32
} from './dto/batch-update.dto';
33
import { BicycleBatchResponse } from './types/BicycleBatchResponse';
0 ignored issues
show
introduced by
'BicycleBatchResponse' is defined but never used.
Loading history...
34
35 8
const BIKE_ID = 'b1e77dd3-9fb9-4e6c-a5c6-b6fc58f59464';
36 8
const BIKE_STATUS_AVAILABLE = 'Available';
0 ignored issues
show
introduced by
'BIKE_STATUS_AVAILABLE' is assigned a value but never used.
Loading history...
37 8
const CREATED_AT = '2024-12-01T05:01:01.000Z';
0 ignored issues
show
introduced by
'CREATED_AT' is assigned a value but never used.
Loading history...
38 8
const UPDATED_AT = '2024-12-07T18:30:30.000Z';
0 ignored issues
show
introduced by
'UPDATED_AT' is assigned a value but never used.
Loading history...
39 8
const UNAUTHORIZED_ERROR_MESSAGE = 'Unauthorized. Authentication required';
40 8
const CITY_ID_GOTHENBURG = '123e4567-e89b-12d3-a456-426614174000';
0 ignored issues
show
introduced by
'CITY_ID_GOTHENBURG' is assigned a value but never used.
Loading history...
41
42
@ApiTags('Bicycles')
43
@Controller({ path: 'bike', version: '1' })
44 8
export class BicyclesController {
45 9
  constructor(private readonly bicyclesService: BicyclesService) {}
46
47
  @Get()
48
  @ApiBearerAuth()
49
  @ApiOperation({ summary: 'Get all bicycles' })
50
  @ApiQuery({
51
    name: 'city',
52
    required: false,
53
    enum: CityName,
54
  })
55
  @ApiQuery({ name: 'lat', required: false, minimum: -90, maximum: 90 })
56
  @ApiQuery({ name: 'lon', required: false, minimum: -180, maximum: 180 })
57
  @ApiQuery({ name: 'radius', required: false, minimum: 0, maximum: 100000 })
58
  @ApiResponse({
59
    status: 200,
60
    description: 'List of bicycles',
61
    type: [Bicycle],
62
  })
63
  @ApiResponse({
64
    status: 401,
65
    description: 'Unauthorized. Authentication required',
66
  })
67 8
  async getAllBicycles(
68
    @Query('lat') lat?: string,
69
    @Query('lon') lon?: string,
70
    @Query('radius') radius?: string,
71
    @Query('city') city?: CityName,
72
  ): Promise<BicycleResponse[]> {
73 4
    const latitude = lat ? parseFloat(lat) : undefined;
74 4
    const longitude = lon ? parseFloat(lon) : undefined;
75 4
    const radi = radius ? parseFloat(radius) : 3000;
76
77 5
    if ((latitude && !longitude) || (longitude && !latitude)) {
78 1
      throw new BadRequestException('Both lat and lon must be provided for location search');
79
    }
80
81 3
    if (city) {
82 1
      if (latitude) {
83
        return this.bicyclesService.toBicycleResponses(
84
          await this.bicyclesService.findByCityAndLocation(city, latitude, longitude, radi),
85
        );
86
      }
87 1
      return this.bicyclesService.toBicycleResponses(await this.bicyclesService.findByCity(city));
88
    }
89
90 2
    if (latitude) {
91 1
      return this.bicyclesService.toBicycleResponses(
92
        await this.bicyclesService.findByLocation(latitude, longitude, radi),
93
      );
94
    }
95
96 1
    return this.bicyclesService.toBicycleResponses(await this.bicyclesService.findAll());
97
  }
98
99
  @Post('create')
100
  @ApiBearerAuth()
101
  @ApiOperation({ summary: 'Create a new bicycle' })
102
  @ApiBody({
103
    type: CreateBicycleDto,
104
    description: 'Bicycle creation data',
105
    required: false,
106
  })
107
  @ApiResponse({
108
    status: 201,
109
    description: 'Bicycle created successfully',
110
    type: Bicycle,
111
  })
112 8
  async createABike(@Body() createBicycleDto: CreateBicycleDto): Promise<Bicycle> {
113 1
    console.log('skapa cykel');
114 1
    return await this.bicyclesService.createBike(createBicycleDto);
115
  }
116
117
  @Post('create-many')
118
  @ApiBearerAuth()
119
  @ApiOperation({
120
    summary: 'Create multiple bicycles',
121
    description:
122
      'Creates multiple bicycles in a single request. At least one bicycle must be provided.',
123
  })
124
  @ApiBody({
125
    type: [CreateBicycleDto],
126
    description: 'Array of bicycle creation data',
127
    required: true,
128
  })
129
  @ApiResponse({
130
    status: 201,
131
    description: 'Bicycles created successfully',
132
    type: [Bicycle],
133
  })
134
  @ApiResponse({
135
    status: 400,
136
    description: 'Bad Request - Empty array or invalid bicycle data provided',
137
  })
138 8
  async createManyBikes(@Body() createBicycleDto: CreateBicycleDto[]): Promise<Bicycle[]> {
139 1
    if (!createBicycleDto?.length) {
140
      throw new BadRequestException('At least one bike is required');
141
    }
142
    return await this.bicyclesService.createManyBikes(createBicycleDto);
143
  }
144
145
  @Get(':bikeId')
146
  @ApiBearerAuth()
147
  @ApiOperation({ summary: 'Get a bicycle by ID' })
148
  @ApiParam({
149
    name: 'bikeId',
150
    description: 'Unique identifier of the bicycle',
151
    type: 'string',
152
    example: BIKE_ID,
153
  })
154
  @ApiResponse({
155
    status: 200,
156
    description: 'Bicycle details retrieved successfully',
157
    type: Bicycle,
158
  })
159
  @ApiResponse({
160
    status: 401,
161
    description: UNAUTHORIZED_ERROR_MESSAGE,
162
  })
163
  @ApiResponse({
164
    status: 404,
165
    description: 'Bicycle not found',
166
  })
167 8
  async getBikeById(@Param('bikeId') id: string): Promise<Bicycle> {
168 1
    return await this.bicyclesService.findById(id);
169
  }
170
171
  @Patch(':bikeId')
172
  @ApiBearerAuth()
173
  @ApiOperation({ summary: 'Update bicycle by ID' })
174
  @ApiParam({
175
    name: 'bikeId',
176
    description: 'Unique identifier of the bicycle',
177
    type: 'string',
178
    example: BIKE_ID,
179
  })
180
  @ApiBody({
181
    description: 'Bicycle update details',
182
    type: UpdateBicycleDto,
183
  })
184
  @ApiResponse({
185
    status: 200,
186
    description: 'Bicycle updated successfully',
187
    type: Bicycle,
188
  })
189
  @ApiResponse({
190
    status: 400,
191
    description: 'Invalid input',
192
  })
193
  @ApiResponse({
194
    status: 404,
195
    description: 'Bicycle not found',
196
  })
197
  @ApiResponse({
198
    status: 401,
199
    description: UNAUTHORIZED_ERROR_MESSAGE,
200
  })
201 8
  async updateBicycle(@Param('bikeId') bikeId: string, @Body() updateBicycleDto: UpdateBicycleDto) {
202 1
    return this.bicyclesService.update(bikeId, updateBicycleDto);
203
  }
204
205
  @Patch('/batch/positions')
206
  @ApiOperation({ summary: 'Update multiple bicycle positions' })
207
  @ApiResponse({
208
    status: 200,
209
    description: 'Bicycle positions updated successfully',
210
    type: BicycleBatchResponseDto,
211
  })
212
  @ApiResponse({
213
    status: 400,
214
    description: 'Error: Bad Request (Invalid request payload)',
215
  })
216 8
  async updateBatchPositions(
217
    @Body() dto: BatchUpdateBicyclePositionsDto,
218
  ): Promise<BicycleBatchResponseDto> {
219 1
    const results = await this.bicyclesService.updatePositionsParallel(dto.updates);
220 1
    return {
221
      results,
222
      totalCount: results.length,
223 2
      successCount: results.filter((r) => r.success).length,
224 2
      failureCount: results.filter((r) => !r.success).length,
225
    };
226
  }
227
228
  @Get('city/:cityName')
229
  @ApiBearerAuth()
230
  @ApiOperation({ summary: 'Get all bicycles in a specific city' })
231
  @ApiResponse({
232
    status: 200,
233
    description: 'List of bicycles in the specified city',
234
    type: [Bicycle],
235
  })
236
  @ApiResponse({
237
    status: 401,
238
    description: UNAUTHORIZED_ERROR_MESSAGE,
239
  })
240
  @ApiParam({
241
    name: 'cityName',
242
    description: 'Name of the city',
243
    type: 'string',
244
    enum: CityName,
245
  })
246 8
  async getBicyclesByCity(@Param('cityName') cityName: CityName): Promise<Bicycle[]> {
247
    return await this.bicyclesService.findByCity(cityName);
248
  }
249
}
250